part of 'win.dart';

class FlutterBluePlusWin {
  // 用于isScanning公共api的流
  static final _isScanning = _StreamController(initialValue: false);

  // 我们总是跟踪这些设备变量
  static final _platformNames = <DeviceIdentifier, String>{};
  static final _serviceUuids = <DeviceIdentifier, List<FBP.Guid>>{};
  static final _manufacturerData = <DeviceIdentifier, Map<int, List<int>>>{};
  static final _advNames = <DeviceIdentifier, String>{};
  static final _rssiMap = <DeviceIdentifier, int?>{};

  // 上次读取的值
  static final Map<DeviceIdentifier, List<StreamSubscription>> _deviceSubscriptions = {};
  static final Map<DeviceIdentifier, List<StreamSubscription>> _delayedSubscriptions = {};
  static final List<StreamSubscription> _scanSubscriptions = [];

  static final Map<DeviceIdentifier, BluetoothDeviceWindows> _devices = {};
  static final Map<DeviceIdentifier, int> _mtuCache = {};

  // 记录每个设备的服务
  static final Map<DeviceIdentifier, List<BluetoothServiceWindows>> _knownServices = {};

  //获取设备，
  static BluetoothDeviceWindows? getDevices(DeviceIdentifier deviceId) {
    return _devices[deviceId];
  }

  //获取设备，
  static BluetoothServiceWindows? getService(DeviceIdentifier deviceId, Guid service) {
    final serviceList = _knownServices[deviceId];
    if (serviceList != null) {
      for (var f in serviceList) {
        if (f.serviceUuid == service) {
          return f;
        }
      }
    }
    return null;
  }

  // 用于scanResults公共api的流
  static final _scanResultsList = _StreamController(initialValue: <ScanResult>[]);

  // 订阅扫描结果流
  static StreamSubscription<BleDevice?>? _scanSubscription;

  // 可通过stopScan取消的扫描超时
  static Timer? _scanTimeout;

  // static final _unhandledDeviceSet = <BluetoothDeviceWindows>{};

  /// Flutter blue plus windows
  static final _connectionStreamController = StreamController<OnConnectionStateMessage>();
  static final _charReadNotificationStreamController = StreamController<OnValueChangeData>();
  static final _scanResultsStreamController = StreamController<BleDevice>();
  static final _discoverServiceStreamController = StreamController<OnDiscoverServicesMessage>();
  static final _setNotifyResultStreamController = StreamController<OnSetNotifyResult>();
  static final _mtuConfigStreamController = StreamController<OnMtuConfigMessage>();
  static final _adapterStateChangedStreamController = StreamController<OnAdapterStateChangedMessage>();
  static final _turnBlueResultStreamController = StreamController<OnTurnBlueMessage>();

  static final _connectionStream = _connectionStreamController.stream.asBroadcastStream();
  static final _charReadNotificationStream = _charReadNotificationStreamController.stream.asBroadcastStream();
  static final _scanResultsStream = _scanResultsStreamController.stream.asBroadcastStream();
  static final _discoverServiceStream = _discoverServiceStreamController.stream.asBroadcastStream();
  static final _setNotifyResultStream = _setNotifyResultStreamController.stream.asBroadcastStream();
  static final _mtuConfigStream = _mtuConfigStreamController.stream.asBroadcastStream();
  static final _adapterStateChangedStream = _adapterStateChangedStreamController.stream.asBroadcastStream();

  //操作Blue 开关
  static final _turnBlueResultChangedStream = _turnBlueResultStreamController.stream.asBroadcastStream();

  static Future<bool> get isSupported async {
    return WinMethodChannelBlue.instance.isSupported();
  }

  static Future<String> get adapterName async {
    return WinMethodChannelBlue.instance.adapterName();
  }

  static Stream<bool> get isScanning => _isScanning.stream;

  static bool get isScanningNow => _isScanning.latestValue;

  static Future<void> turnOn({int timeout = 60}) async {
    await WinMethodChannelBlue.instance.openBlue();
    await FlutterBluePlusWin._turnBlueResultChangedStream.map((it) => it.turnIsOn == true).first.timeout(Duration(seconds: timeout));
    return;
  }

  static Future<void> turnOff({int timeout = 60}) async {
    await WinMethodChannelBlue.instance.offBlue();
    await FlutterBluePlusWin._turnBlueResultChangedStream.map((it) => it.turnIsOn == false).first.timeout(Duration(seconds: timeout));
    return;
  }

  static Stream<List<ScanResult>> get scanResults => _scanResultsList.stream;

  //单次结果
  static Future<BluetoothAdapterState> get adapterStateFuture async {
    final result = await WinMethodChannelBlue.instance.isBluetoothAvailable();
    return result ? BluetoothAdapterState.on : BluetoothAdapterState.off;
    // await _initialize();
    // yield _state;
    // yield* WinBle.bleState.asBroadcastStream().map(
    //   (s) {
    //     _state = s.toAdapterState();
    //     return _state;
    //   },
    // );
  }

  static BluetoothAdapterState? _adapterStateNow;

  //适配器状态
  static Stream<BluetoothAdapterState> get adapterState async* {
    if (_adapterStateNow == null) {
      var result = await adapterStateFuture;
      if (_adapterStateNow == null) {
        _adapterStateNow = result;
      }
    }
    yield* FlutterBluePlusWin._adapterStateChangedStream.map((it) => it.state).newStreamWithInitialValue(_adapterStateNow!);
  }

  static Map<DeviceIdentifier, FBP.BluetoothDevice> get devices {
    return _devices;
  }

  // static Future<List<BluetoothDeviceWindows>> get bondedDevices async {
  //   return _devices;
  // }

  /// Start a scan, and return a stream of results
  /// Note: scan filters use an "or" behavior. i.e. if you set `withServices` & `withNames` we
  /// return all the advertisments that match any of the specified services *or* any of the specified names.
  ///   - [withServices] filter by advertised services
  ///   - [withRemoteIds] filter for known remoteIds (iOS: 128-bit guid, android: 48-bit mac address)
  ///   - [withNames] filter by advertised names (exact match)
  ///   - [withKeywords] filter by advertised names (matches any substring)
  ///   - [withMsd] filter by manfacture specific data
  ///   - [withServiceData] filter by service data
  ///   - [timeout] calls stopScan after a specified duration
  ///   - [removeIfGone] if true, remove devices after they've stopped advertising for X duration
  ///   - [continuousUpdates] If `true`, we continually update 'lastSeen' & 'rssi' by processing
  ///        duplicate advertisements. This takes more power. You typically should not use this option.
  ///   - [continuousDivisor] Useful to help performance. If divisor is 3, then two-thirds of advertisements are
  ///        ignored, and one-third are processed. This reduces main-thread usage caused by the platform channel.
  ///        The scan counting is per-device so you always get the 1st advertisement from each device.
  ///        If divisor is 1, all advertisements are returned. This argument only matters for `continuousUpdates` mode.
  ///   - [oneByOne] if `true`, we will stream every advertistment one by one, possibly including duplicates.
  ///        If `false`, we deduplicate the advertisements, and return a list of devices.
  ///   - [androidLegacy] Android only. If `true`, scan on 1M phy only.
  ///        If `false`, scan on all supported phys. How the radio cycles through all the supported phys is purely
  ///        dependent on the your Bluetooth stack implementation.
  ///   - [androidScanMode] choose the android scan mode to use when scanning
  ///   - [androidUsesFineLocation] request `ACCESS_FINE_LOCATION` permission at runtime
  static Future<void> startScan({
    List<Guid> withServices = const [],
    List<String> withRemoteIds = const [],
    List<String> withNames = const [],
    //TODO: implementation missing
    List<String> withKeywords = const [],
    //TODO: implementation missing
    List<MsdFilter> withMsd = const [],
    List<ServiceDataFilter> withServiceData = const [],
    Duration? timeout,
    Duration? removeIfGone,
    bool continuousUpdates = false,
    int continuousDivisor = 1,
    bool oneByOne = false,
    bool androidLegacy = false,
    AndroidScanMode androidScanMode = AndroidScanMode.lowLatency,
    bool androidUsesFineLocation = false,
  }) async {
    // 停止现有扫描
    if (_isScanning.latestValue == true) {
      await stopScan();
    }
    // 推送至流媒体
    _isScanning.add(true);
    // 在监听流后启动计时器，以确保
    //在设置_buffer之前，超时不会触发
    if (timeout != null) {
      _scanTimeout = Timer(timeout, stopScan);
    }

    ///通过操作系统删除连接。
    ///我们添加此逻辑的原因是
    ///以避免不可控的设备并保持一致性。
    ///添加WinBle扫描
    WinMethodChannelBlue.instance.startScan();

    final Stream<BleDevice> outputStream = _scanResultsStream;
    final output = <ScanResult>[];
    // 收听并推送到“scanResults”流
    _scanSubscription = outputStream.listen(
      (BleDevice winBleDevice) {
        // log("ScanResult" + winBleDevice.toJson().toString());
        final remoteId = DeviceIdentifier(winBleDevice.address.toUpperCase());
        final scanResult = output.where((sr) => sr.device.remoteId == remoteId).firstOrNull;
        final deviceName = winBleDevice.name.isNotEmpty ? winBleDevice.name : scanResult?.device.platformName ?? FlutterBluePlusWin._platformNames[remoteId] ?? "";
        final serviceUuids = winBleDevice.serviceUuids.isNotEmpty
            ? [...winBleDevice.serviceUuids.map((e) => Guid((e).replaceAll(RegExp(r'[{}]'), '')))]
            : scanResult?.advertisementData.serviceUuids ?? _serviceUuids[remoteId] ?? [];

        final manufacturerData = winBleDevice.manufacturerData.isNotEmpty
            ? {
                if (winBleDevice.manufacturerData.length >= 2) winBleDevice.manufacturerData[0] + (winBleDevice.manufacturerData[1] << 8): winBleDevice.manufacturerData.sublist(2),
              }
            : scanResult?.advertisementData.manufacturerData ?? _manufacturerData[remoteId] ?? {};

        final rssi = int.tryParse(winBleDevice.rssi) ?? -100;

        if (deviceName.isNotEmpty) {
          FlutterBluePlusWin._platformNames[remoteId] = deviceName;
          FlutterBluePlusWin._advNames[remoteId] = deviceName;
        }
        if (serviceUuids.isNotEmpty) {
          _serviceUuids[remoteId] = serviceUuids;
        }
        if (manufacturerData.isNotEmpty) {
          _manufacturerData[remoteId] = manufacturerData;
        }
        // log("ScanResult 222 " + deviceName + serviceUuids.toString());
        FlutterBluePlusWin._rssiMap[remoteId] = rssi;

        final device = BluetoothDeviceWindows(remoteId: remoteId);

        String hex(int value) => value.toRadixString(16).padLeft(2, '0');
        String hexToId(Iterable<int> values) => values.map((e) => hex(e)).join();

        final sr = ScanResult(
          device: device,
          advertisementData: AdvertisementData(
            advName: deviceName,
            txPowerLevel: winBleDevice.adStructures?.where((e) => e.type == 10).singleOrNull?.data.firstOrNull,
            //TODO: 应验证
            connectable: !winBleDevice.advType.contains('Non'),
            manufacturerData: manufacturerData,
            serviceData: {
              for (final advStructures in winBleDevice.adStructures ?? <AdStructure>[])
                if (advStructures.type == 0x16 && advStructures.data.length >= 2) Guid(hexToId(advStructures.data.sublist(0, 2).reversed)): advStructures.data.sublist(2),
              for (final advStructures in winBleDevice.adStructures ?? <AdStructure>[])
                if (advStructures.type == 0x20 && advStructures.data.length >= 4) Guid(hexToId(advStructures.data.sublist(0, 4).reversed)): advStructures.data.sublist(4),
              for (final advStructures in winBleDevice.adStructures ?? <AdStructure>[])
                if (advStructures.type == 0x21 && advStructures.data.length >= 16) Guid(hexToId(advStructures.data.sublist(0, 16).reversed)): advStructures.data.sublist(16),
            },
            serviceUuids: serviceUuids,
            appearance: null,
          ),
          rssi: rssi,
          timeStamp: DateTime.now(),
        );

        // 按服务筛选
        final isFilteredWithServices = withServices.isNotEmpty && serviceUuids.where((service) => withServices.contains(service)).isEmpty;
        // 使用远程id进行筛选
        final isFilteredWithRemoteIds = withRemoteIds.isNotEmpty && !withRemoteIds.contains(remoteId);
        // 带名称的筛选器
        final isFilteredWithNames = withNames.isNotEmpty && !withNames.contains(deviceName);
        if (isFilteredWithServices || isFilteredWithRemoteIds || isFilteredWithNames) {
          _scanResultsList.add(List.from(output));
          return;
        }

        // 将结果添加到输出中
        if (oneByOne) {
          output
            ..clear()
            ..add(sr);
        } else {
          output.addOrUpdate(sr);
        }

        // 推送至流媒体
        _scanResultsList.add(List.from(output));
      },
    );
  }

  /// 停止扫描蓝牙低功耗设备
  static Future<void> stopScan() async {
    WinMethodChannelBlue.instance.stopScan();
    _scanSubscription?.cancel();
    _scanTimeout?.cancel();
    _isScanning.add(false);

    for (var subscription in _scanSubscriptions) {
      subscription.cancel();
    }

    _scanResultsList.latestValue = [];
  }

  ///注册一个订阅，以便在扫描完成时取消。
  ///此函数简化了清理，因此您可以防止创建重复的流订阅。
  ///-这是一个可选的便利功能
  ///-防止在每次扫描之前意外创建重复订阅
  static void cancelWhenScanComplete(StreamSubscription subscription) {
    _scanSubscriptions.add(subscription);
  }

  /// 设置FlutterBlue内部日志级别
  static Future<void> setLogLevel(LogLevel level, {color = true}) async {
    return;
  }

  static Future<bool> get isOn async {
    return true;
    // return await WinBle.bleState.asBroadcastStream().first == BleState.On;
  }

  //接收原生连接状态事件
  static void _onConnectivityChanged(OnConnectionStateMessage data) {
    final connected = data.success && data.state == BluetoothConnectionState.connected;
    if (!connected) {
      final rmDevice = FlutterBluePlusWin._devices.remove(data.deviceId);
      if (rmDevice != null) {
        rmDevice._updateState(BluetoothConnectionState.disconnected);
        _deviceSubscriptions[rmDevice.remoteId]?.forEach((s) => s.cancel());
        _deviceSubscriptions.remove(rmDevice.remoteId);
        // use delayed to update the stream before we cancel it
        Future.delayed(Duration.zero).then((_) {
          _delayedSubscriptions[rmDevice.remoteId]?.forEach((s) => s.cancel());
          _delayedSubscriptions.remove(rmDevice.remoteId);
        });
      }
    } else {
      final device = FlutterBluePlusWin._devices[data.deviceId];
      if (device != null) {
        device._updateState(BluetoothConnectionState.connected);
      }
    }
    //全部处理完成再通知结果
    _connectionStreamController.add(data);
  }

  ///接收原生通知值回调事件,,接收原生读取值回调事件
  static void _onValueChanged(OnValueChangeData data) {
    if (data.success) {
      getService(data.deviceId, data.serviceUuid)?.getCharacteristic(data.characteristicUuid)?._value = data.value;
      _charReadNotificationStreamController.add(data);
    }
  }

  ///接收原生发现服务回调事件
  static void _onServiceDiscovered(OnDiscoverServicesMessage data) {
    if (data.success) {
      _knownServices[data.deviceId] = data.service;
    }
    _discoverServiceStreamController.add(data);
  }
}
